home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / difutl27.zip / difutl27 / sdiff.c < prev    next >
C/C++ Source or Header  |  1994-09-30  |  25KB  |  1,181 lines

  1. /* SDIFF -- interactive merge front end to diff
  2.    Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* GNU SDIFF was written by Thomas Lord. */
  21.  
  22. #include "system.h"
  23. #include <stdio.h>
  24. #include <signal.h>
  25. #include "getopt.h"
  26.  
  27. /* Size of chunks read from files which must be parsed into lines. */
  28. #define SDIFF_BUFSIZE ((size_t) 65536)
  29.  
  30. /* Default name of the diff program */
  31. #ifndef DIFF_PROGRAM
  32. #define DIFF_PROGRAM "/usr/bin/diff"
  33. #endif
  34.  
  35. /* Users' editor of nonchoice */
  36. #ifndef DEFAULT_EDITOR_PROGRAM
  37. #define DEFAULT_EDITOR_PROGRAM "ed"
  38. #endif
  39.  
  40. extern char version_string[];
  41. static char const *program_name;
  42. static char const *diffbin = DIFF_PROGRAM;
  43. static char const *edbin = DEFAULT_EDITOR_PROGRAM;
  44. static char const **diffargv;
  45.  
  46. static char *tmpname;
  47. static int volatile tmpmade;
  48.  
  49. #if HAVE_FORK
  50. static pid_t volatile diffpid;
  51. #endif
  52.  
  53. struct line_filter;
  54.  
  55. static FILE *ck_fopen PARAMS((char const *, char const *));
  56. static RETSIGTYPE catchsig PARAMS((int));
  57. static VOID *xmalloc PARAMS((size_t));
  58. static char const *expand_name PARAMS((char *, int, char const *));
  59. static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*));
  60. static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*));
  61. static int lf_snarf PARAMS((struct line_filter *, char *, size_t));
  62. static int skip_white PARAMS((void));
  63. static size_t ck_fread PARAMS((char *, size_t, FILE *));
  64. static size_t lf_refill PARAMS((struct line_filter *));
  65. static void checksigs PARAMS((void));
  66. static void ck_fclose PARAMS((FILE *));
  67. static void ck_fflush PARAMS((FILE *));
  68. static void ck_fwrite PARAMS((char const *, size_t, FILE *));
  69. static void cleanup PARAMS((void));
  70. static void diffarg PARAMS((char const *));
  71. static void execdiff PARAMS((void));
  72. static void exiterr PARAMS((void));
  73. static void fatal PARAMS((char const *));
  74. static void flush_line PARAMS((void));
  75. static void give_help PARAMS((void));
  76. static void lf_copy PARAMS((struct line_filter *, int, FILE *));
  77. static void lf_init PARAMS((struct line_filter *, FILE *));
  78. static void lf_skip PARAMS((struct line_filter *, int));
  79. static void perror_fatal PARAMS((char const *));
  80. static void trapsigs PARAMS((void));
  81. static void try_help PARAMS((char const *));
  82. static void untrapsig PARAMS((int));
  83. static void usage PARAMS((void));
  84.  
  85. /* this lossage until the gnu libc conquers the universe */
  86. #if HAVE_TMPNAM
  87. #define private_tempnam() tmpnam ((char *) 0)
  88. #else
  89. #ifndef PVT_tmpdir
  90. #define PVT_tmpdir "/tmp"
  91. #endif
  92. #ifndef TMPDIR_ENV
  93. #define TMPDIR_ENV "TMPDIR"
  94. #endif
  95. static char *private_tempnam PARAMS((void));
  96. static int exists PARAMS((char const *));
  97. #endif
  98. static int diraccess PARAMS((char const *));
  99.  
  100. /* Options: */
  101.  
  102. /* name of output file if -o spec'd */
  103. static char *out_file;
  104.  
  105. /* do not print common lines if true, set by -s option */
  106. static int suppress_common_flag;
  107.  
  108. static struct option const longopts[] =
  109. {
  110.   {"ignore-blank-lines", 0, 0, 'B'},
  111.   {"speed-large-files", 0, 0, 'H'},
  112.   {"ignore-matching-lines", 1, 0, 'I'},
  113.   {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
  114.   {"text", 0, 0, 'a'},
  115.   {"ignore-space-change", 0, 0, 'b'},
  116.   {"minimal", 0, 0, 'd'},
  117.   {"ignore-case", 0, 0, 'i'},
  118.   {"left-column", 0, 0, 'l'},
  119.   {"output", 1, 0, 'o'},
  120.   {"suppress-common-lines", 0, 0, 's'},
  121.   {"expand-tabs", 0, 0, 't'},
  122.   {"width", 1, 0, 'w'},
  123.   {"version", 0, 0, 'v'},
  124.   {"help", 0, 0, 129},
  125.   {0, 0, 0, 0}
  126. };
  127.  
  128. static void
  129. try_help (reason)
  130.      char const *reason;
  131. {
  132.   if (reason)
  133.     fprintf (stderr, "%s: %s\n", program_name, reason);
  134.   fprintf (stderr, "%s: Try `%s --help' for more information.\n",
  135.        program_name, program_name);
  136.   exit (2);
  137. }
  138.  
  139. static void
  140. usage ()
  141. {
  142.   printf ("Usage: %s [OPTIONS]... FILE1 FILE2\n\n", program_name);
  143.   printf ("%s", "\
  144.   -o FILE  --output=FILE  Operate interactively, sending output to FILE.\n\n");
  145.   printf ("%s", "\
  146.   -i  --ignore-case  Consider upper- and lower-case to be the same.\n\
  147.   -W  --ignore-all-space  Ignore all white space.\n\
  148.   -b  --ignore-space-change  Ignore changes in the amount of white space.\n\
  149.   -B  --ignore-blank-lines  Ignore changes whose lines are all blank.\n\
  150.   -I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE.\n\
  151.   -a  --text  Treat all files as text.\n\n");
  152.   printf ("%s", "\
  153.   -w NUM  --width=NUM  Output at most NUM (default 130) characters per line.\n\
  154.   -l  --left-column  Output only the left column of common lines.\n\
  155.   -s  --suppress-common-lines  Do not output common lines.\n\n");
  156.   printf ("\
  157.   -t  --expand-tabs  Expand tabs to spaces in output.\n\n");
  158.   printf ("%s", "\
  159.   -d  --minimal  Try hard to find a smaller set of changes.\n\
  160.   -H  --speed-large-files  Assume large files and many scattered small changes.\n\n");
  161.  printf ("%s", "\
  162.   -v  --version  Output version info.\n\
  163.   --help  Output this help.\n\n\
  164. If FILE1 or FILE2 is `-', read standard input.\n");
  165. }
  166.  
  167. static void
  168. cleanup ()
  169. {
  170. #if HAVE_FORK
  171.   if (0 < diffpid)
  172.     kill (diffpid, SIGPIPE);
  173. #endif
  174.   if (tmpmade)
  175.     unlink (tmpname);
  176. }
  177.  
  178. static void
  179. exiterr ()
  180. {
  181.   cleanup ();
  182.   untrapsig (0);
  183.   checksigs ();
  184.   exit (2);
  185. }
  186.  
  187. static void
  188. fatal (msg)
  189.      char const *msg;
  190. {
  191.   fprintf (stderr, "%s: %s\n", program_name, msg);
  192.   exiterr ();
  193. }
  194.  
  195. static void
  196. perror_fatal (msg)
  197.      char const *msg;
  198. {
  199.   int e = errno;
  200.   checksigs ();
  201.   fprintf (stderr, "%s: ", program_name);
  202.   errno = e;
  203.   perror (msg);
  204.   exiterr ();
  205. }
  206.  
  207.  
  208. /* malloc freely or DIE! */
  209. static VOID *
  210. xmalloc (size)
  211.      size_t size;
  212. {
  213.   VOID *r = (VOID *) malloc (size);
  214.   if (!r)
  215.     fatal ("memory exhausted");
  216.   return r;
  217. }
  218.  
  219. static FILE *
  220. ck_fopen (fname, type)
  221.      char const *fname, *type;
  222. {
  223.   FILE *r = fopen (fname, type);
  224.   if (!r)
  225.     perror_fatal (fname);
  226.   return r;
  227. }
  228.  
  229. static void
  230. ck_fclose (f)
  231.      FILE *f;
  232. {
  233.   if (fclose (f))
  234.     perror_fatal ("input/output error");
  235. }
  236.  
  237. static size_t
  238. ck_fread (buf, size, f)
  239.      char *buf;
  240.      size_t size;
  241.      FILE *f;
  242. {
  243.   size_t r = fread (buf, sizeof (char), size, f);
  244.   if (r == 0 && ferror (f))
  245.     perror_fatal ("input error");
  246.   return r;
  247. }
  248.  
  249. static void
  250. ck_fwrite (buf, size, f)
  251.      char const *buf;
  252.      size_t size;
  253.      FILE *f;
  254. {
  255.   if (fwrite (buf, sizeof (char), size, f) != size)
  256.     perror_fatal ("output error");
  257. }
  258.  
  259. static void
  260. ck_fflush (f)
  261.      FILE *f;
  262. {
  263.   if (fflush (f) != 0)
  264.     perror_fatal ("output error");
  265. }
  266.  
  267. static char const *
  268. expand_name (name, is_dir, other_name)
  269.      char *name;
  270.      int is_dir;
  271.      char const *other_name;
  272. {
  273.   if (strcmp (name, "-") == 0)
  274.     fatal ("cannot interactively merge standard input");
  275.   if (!is_dir)
  276.     return name;
  277.   else
  278.     {
  279.       /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
  280.       char const *p = filename_lastdirchar (other_name);
  281.       char const *base = p ? p+1 : other_name;
  282.       size_t namelen = strlen (name), baselen = strlen (base);
  283.       char *r = xmalloc (namelen + baselen + 2);
  284.       memcpy (r, name, namelen);
  285.       r[namelen] = '/';
  286.       memcpy (r + namelen + 1, base, baselen + 1);
  287.       return r;
  288.     }
  289. }
  290.  
  291.  
  292.  
  293. struct line_filter {
  294.   FILE *infile;
  295.   char *bufpos;
  296.   char *buffer;
  297.   char *buflim;
  298. };
  299.  
  300. static void
  301. lf_init (lf, infile)
  302.      struct line_filter *lf;
  303.      FILE *infile;
  304. {
  305.   lf->infile = infile;
  306.   lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
  307.   lf->buflim[0] = '\n';
  308. }
  309.  
  310. /* Fill an exhausted line_filter buffer from its INFILE */
  311. static size_t
  312. lf_refill (lf)
  313.      struct line_filter *lf;
  314. {
  315.   size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
  316.   lf->bufpos = lf->buffer;
  317.   lf->buflim = lf->buffer + s;
  318.   lf->buflim[0] = '\n';
  319.   checksigs ();
  320.   return s;
  321. }
  322.  
  323. /* Advance LINES on LF's infile, copying lines to OUTFILE */
  324. static void
  325. lf_copy (lf, lines, outfile)
  326.      struct line_filter *lf;
  327.      int lines;
  328.      FILE *outfile;
  329. {
  330.   char *start = lf->bufpos;
  331.  
  332.   while (lines)
  333.     {
  334.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  335.       if (! lf->bufpos)
  336.     {
  337.       ck_fwrite (start, lf->buflim - start, outfile);
  338.       if (! lf_refill (lf))
  339.         return;
  340.       start = lf->bufpos;
  341.     }
  342.       else
  343.     {
  344.       --lines;
  345.       ++lf->bufpos;
  346.     }
  347.     }
  348.  
  349.   ck_fwrite (start, lf->bufpos - start, outfile);
  350. }
  351.  
  352. /* Advance LINES on LF's infile without doing output */
  353. static void
  354. lf_skip (lf, lines)
  355.      struct line_filter *lf;
  356.      int lines;
  357. {
  358.   while (lines)
  359.     {
  360.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  361.       if (! lf->bufpos)
  362.     {
  363.       if (! lf_refill (lf))
  364.         break;
  365.     }
  366.       else
  367.     {
  368.       --lines;
  369.       ++lf->bufpos;
  370.     }
  371.     }
  372. }
  373.  
  374. /* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
  375. static int
  376. lf_snarf (lf, buffer, bufsize)
  377.      struct line_filter *lf;
  378.      char *buffer;
  379.      size_t bufsize;
  380. {
  381.   char *start = lf->bufpos;
  382.  
  383.   for (;;)
  384.     {
  385.       char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
  386.       size_t s = next - start;
  387.       if (bufsize <= s)
  388.     return 0;
  389.       memcpy (buffer, start, s);
  390.       if (next < lf->buflim)
  391.     {
  392.       buffer[s] = 0;
  393.       lf->bufpos = next + 1;
  394.       return 1;
  395.     }
  396.       if (! lf_refill (lf))
  397.     return s ? 0 : EOF;
  398.       buffer += s;
  399.       bufsize -= s;
  400.       start = next;
  401.     }
  402. }
  403.  
  404.  
  405.  
  406. int
  407. main (argc, argv)
  408.      int argc;
  409.      char *argv[];
  410. {
  411.   int opt;
  412.   char *editor;
  413.   char *differ;
  414.  
  415.   initialize_main (&argc, &argv);
  416.   program_name = argv[0];
  417.  
  418.   editor = getenv ("EDITOR");
  419.   if (editor)
  420.     edbin = editor;
  421.   differ = getenv ("DIFF");
  422.   if (differ)
  423.     diffbin = differ;
  424.  
  425.   diffarg ("diff");
  426.  
  427.   /* parse command line args */
  428.   while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
  429.      != EOF)
  430.     {
  431.       switch (opt)
  432.     {
  433.     case 'a':
  434.       diffarg ("-a");
  435.       break;
  436.  
  437.     case 'b':
  438.       diffarg ("-b");
  439.       break;
  440.  
  441.     case 'B':
  442.       diffarg ("-B");
  443.       break;
  444.  
  445.     case 'd':
  446.       diffarg ("-d");
  447.       break;
  448.  
  449.     case 'H':
  450.       diffarg ("-H");
  451.       break;
  452.  
  453.     case 'i':
  454.       diffarg ("-i");
  455.       break;
  456.  
  457.     case 'I':
  458.       diffarg ("-I");
  459.       diffarg (optarg);
  460.       break;
  461.  
  462.     case 'l':
  463.       diffarg ("--left-column");
  464.       break;
  465.  
  466.     case 'o':
  467.       out_file = optarg;
  468.       break;
  469.  
  470.     case 's':
  471.       suppress_common_flag = 1;
  472.       break;
  473.  
  474.     case 't':
  475.       diffarg ("-t");
  476.       break;
  477.  
  478.     case 'v':
  479.       printf ("sdiff - GNU diffutils version %s\n", version_string);
  480.       exit (0);
  481.  
  482.     case 'w':
  483.       diffarg ("-W");
  484.       diffarg (optarg);
  485.       break;
  486.  
  487.     case 'W':
  488.       diffarg ("-w");
  489.       break;
  490.  
  491.     case 129:
  492.       usage ();
  493.       if (ferror (stdout) || fclose (stdout) != 0)
  494.         fatal ("write error");
  495.       exit (0);
  496.  
  497.     default:
  498.       try_help (0);
  499.     }
  500.     }
  501.  
  502.   if (argc - optind != 2)
  503.     try_help (argc - optind < 2 ? "missing operand" : "extra operand");
  504.  
  505.   if (! out_file)
  506.     {
  507.       /* easy case: diff does everything for us */
  508.       if (suppress_common_flag)
  509.     diffarg ("--suppress-common-lines");
  510.       diffarg ("-y");
  511.       diffarg ("--");
  512.       diffarg (argv[optind]);
  513.       diffarg (argv[optind + 1]);
  514.       diffarg (0);
  515.       execdiff ();
  516.     }
  517.   else
  518.     {
  519.       FILE *left, *right, *out, *diffout;
  520.       int interact_ok;
  521.       struct line_filter lfilt;
  522.       struct line_filter rfilt;
  523.       struct line_filter diff_filt;
  524.       int leftdir = diraccess (argv[optind]);
  525.       int rightdir = diraccess (argv[optind + 1]);
  526.  
  527.       if (leftdir && rightdir)
  528.     fatal ("both files to be compared are directories");
  529.  
  530.       left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
  531.       ;
  532.       right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
  533.       out = ck_fopen (out_file, "w");
  534.  
  535.       diffarg ("--sdiff-merge-assist");
  536.       diffarg ("--");
  537.       diffarg (argv[optind]);
  538.       diffarg (argv[optind + 1]);
  539.       diffarg (0);
  540.  
  541.       trapsigs ();
  542.  
  543. #if ! HAVE_FORK
  544.       {
  545.     size_t cmdsize = 1;
  546.     char *p, *command;
  547.     int i;
  548.  
  549.     for (i = 0;  diffargv[i];  i++)
  550.       cmdsize += 4 * strlen (diffargv[i]) + 3;
  551.     command = p = xmalloc (cmdsize);
  552.     for (i = 0;  diffargv[i];  i++)
  553.       {
  554.         char const *a = diffargv[i];
  555.         SYSTEM_QUOTE_ARG (p, a);
  556.         *p++ = ' ';
  557.       }
  558.     p[-1] = '\0';
  559.     diffout = popen (command, "r");
  560.     if (!diffout)
  561.       perror_fatal (command);
  562.     free (command);
  563.       }
  564. #else /* HAVE_FORK */
  565.       {
  566.     int diff_fds[2];
  567.  
  568.     if (pipe (diff_fds) != 0)
  569.       perror_fatal ("pipe");
  570.  
  571.     diffpid = vfork ();
  572.     if (diffpid < 0)
  573.       perror_fatal ("fork failed");
  574.     if (!diffpid)
  575.       {
  576.         signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
  577.         signal (SIGPIPE, SIG_DFL);
  578.  
  579.         close (diff_fds[0]);
  580.         if (diff_fds[1] != STDOUT_FILENO)
  581.           {
  582.         dup2 (diff_fds[1], STDOUT_FILENO);
  583.         close (diff_fds[1]);
  584.           }
  585.  
  586.         execdiff ();
  587.       }
  588.  
  589.     close (diff_fds[1]);
  590.     diffout = fdopen (diff_fds[0], "r");
  591.     if (!diffout)
  592.       perror_fatal ("fdopen");
  593.       }
  594. #endif /* HAVE_FORK */
  595.  
  596.       lf_init (&diff_filt, diffout);
  597.       lf_init (&lfilt, left);
  598.       lf_init (&rfilt, right);
  599.  
  600.       interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
  601.  
  602.       ck_fclose (left);
  603.       ck_fclose (right);
  604.       ck_fclose (out);
  605.  
  606.       {
  607.     int wstatus;
  608.  
  609. #if ! HAVE_FORK
  610.     wstatus = pclose (diffout);
  611. #else
  612.     ck_fclose (diffout);
  613.     while (waitpid (diffpid, &wstatus, 0) < 0)
  614.       if (errno == EINTR)
  615.         checksigs ();
  616.       else
  617.         perror_fatal ("wait failed");
  618.     diffpid = 0;
  619. #endif
  620.  
  621.     if (tmpmade)
  622.       {
  623.         unlink (tmpname);
  624.         tmpmade = 0;
  625.       }
  626.  
  627.     if (! interact_ok)
  628.       exiterr ();
  629.  
  630.     if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
  631.       fatal ("Subsidiary diff failed");
  632.  
  633.     untrapsig (0);
  634.     checksigs ();
  635.     exit (WEXITSTATUS (wstatus));
  636.       }
  637.     }
  638.   return 0;            /* Fool -Wall . . . */
  639. }
  640.  
  641. static void
  642. diffarg (a)
  643.      char const *a;
  644. {
  645.   static unsigned diffargs, diffargsmax;
  646.  
  647.   if (diffargs == diffargsmax)
  648.     {
  649.       if (! diffargsmax)
  650.     {
  651.       diffargv = (char const **) xmalloc (sizeof (char));
  652.       diffargsmax = 8;
  653.     }
  654.       diffargsmax *= 2;
  655.       diffargv = (char const **) realloc (diffargv,
  656.                       diffargsmax * sizeof (char const *));
  657.       if (! diffargv)
  658.     fatal ("out of memory");
  659.     }
  660.   diffargv[diffargs++] = a;
  661. }
  662.  
  663. static void
  664. execdiff ()
  665. {
  666.   execvp (diffbin, (char **) diffargv);
  667.   write (STDERR_FILENO, diffbin, strlen (diffbin));
  668.   write (STDERR_FILENO, ": not found\n", 12);
  669.   _exit (2);
  670. }
  671.  
  672.  
  673.  
  674.  
  675. /* Signal handling */
  676.  
  677. #define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
  678. static int const sigs[] = {
  679. #ifdef SIGHUP
  680.        SIGHUP,
  681. #endif
  682. #ifdef SIGQUIT
  683.        SIGQUIT,
  684. #endif
  685. #ifdef SIGTERM
  686.        SIGTERM,
  687. #endif
  688. #ifdef SIGXCPU
  689.        SIGXCPU,
  690. #endif
  691. #ifdef SIGXFSZ
  692.        SIGXFSZ,
  693. #endif
  694.        SIGINT,
  695.        SIGPIPE
  696. };
  697.  
  698. /* Prefer `sigaction' if it is available, since `signal' can lose signals.  */
  699. #if HAVE_SIGACTION
  700. static struct sigaction initial_action[NUM_SIGS];
  701. #define initial_handler(i) (initial_action[i].sa_handler)
  702. #else
  703. static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
  704. #define initial_handler(i) (initial_action[i])
  705. #endif
  706.  
  707. static int volatile ignore_SIGINT;
  708. static int volatile signal_received;
  709. static int sigs_trapped;
  710.  
  711. static RETSIGTYPE
  712. catchsig (s)
  713.      int s;
  714. {
  715. #if ! HAVE_SIGACTION
  716.   signal (s, SIG_IGN);
  717. #endif
  718.   if (! (s == SIGINT && ignore_SIGINT))
  719.     signal_received = s;
  720. }
  721.  
  722. static void
  723. trapsigs ()
  724. {
  725.   int i;
  726.  
  727. #if HAVE_SIGACTION
  728.   struct sigaction catchaction;
  729.   bzero (&catchaction, sizeof (catchaction));
  730.   catchaction.sa_handler = catchsig;
  731. #ifdef SA_INTERRUPT
  732.   /* Non-Posix BSD-style systems like SunOS 4.1.x need this
  733.      so that `read' calls are interrupted properly.  */
  734.   catchaction.sa_flags = SA_INTERRUPT;
  735. #endif
  736.   sigemptyset (&catchaction.sa_mask);
  737.   for (i = 0;  i < NUM_SIGS;  i++)
  738.     sigaddset (&catchaction.sa_mask, sigs[i]);
  739.   for (i = 0;  i < NUM_SIGS;  i++)
  740.     {
  741.       sigaction (sigs[i], 0, &initial_action[i]);
  742.       if (initial_handler (i) != SIG_IGN
  743.       && sigaction (sigs[i], &catchaction, 0) != 0)
  744.     fatal ("signal error");
  745.     }
  746. #else /* ! HAVE_SIGACTION */
  747.   for (i = 0;  i < NUM_SIGS;  i++)
  748.     {
  749.       initial_action[i] = signal (sigs[i], SIG_IGN);
  750.       if (initial_handler (i) != SIG_IGN
  751.       && signal (sigs[i], catchsig) != SIG_IGN)
  752.     fatal ("signal error");
  753.     }
  754. #endif /* ! HAVE_SIGACTION */
  755.  
  756. #if !defined(SIGCHLD) && defined(SIGCLD)
  757. #define SIGCHLD SIGCLD
  758. #endif
  759. #ifdef SIGCHLD
  760.   /* System V fork+wait does not work if SIGCHLD is ignored.  */
  761.   signal (SIGCHLD, SIG_DFL);
  762. #endif
  763.  
  764.   sigs_trapped = 1;
  765. }
  766.  
  767. /* Untrap signal S, or all trapped signals if S is zero.  */
  768. static void
  769. untrapsig (s)
  770.      int s;
  771. {
  772.   int i;
  773.  
  774.   if (sigs_trapped)
  775.     for (i = 0;  i < NUM_SIGS;  i++)
  776.       if ((!s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
  777. #if HAVE_SIGACTION
  778.       sigaction (sigs[i], &initial_action[i], 0);
  779. #else
  780.       signal (sigs[i], initial_action[i]);
  781. #endif
  782. }
  783.  
  784. /* Exit if a signal has been received.  */
  785. static void
  786. checksigs ()
  787. {
  788.   int s = signal_received;
  789.   if (s)
  790.     {
  791.       cleanup ();
  792.  
  793.       /* Yield an exit status indicating that a signal was received.  */
  794.       untrapsig (s);
  795.       kill (getpid (), s);
  796.  
  797.       /* That didn't work, so exit with error status.  */
  798.       exit (2);
  799.     }
  800. }
  801.  
  802.  
  803.  
  804. static void
  805. give_help ()
  806. {
  807.   fprintf (stderr,"l:\tuse the left version\n");
  808.   fprintf (stderr,"r:\tuse the right version\n");
  809.   fprintf (stderr,"e l:\tedit then use the left version\n");
  810.   fprintf (stderr,"e r:\tedit then use the right version\n");
  811.   fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n");
  812.   fprintf (stderr,"e:\tedit a new version\n");
  813.   fprintf (stderr,"s:\tsilently include common lines\n");
  814.   fprintf (stderr,"v:\tverbosely include common lines\n");
  815.   fprintf (stderr,"q:\tquit\n");
  816. }
  817.  
  818. static int
  819. skip_white ()
  820. {
  821.   int c;
  822.   for (;;)
  823.     {
  824.       c = getchar ();
  825.       if (!ISSPACE (c) || c == '\n')
  826.     break;
  827.       checksigs ();
  828.     }
  829.   if (ferror (stdin))
  830.     perror_fatal ("input error");
  831.   return c;
  832. }
  833.  
  834. static void
  835. flush_line ()
  836. {
  837.   int c;
  838.   while ((c = getchar ()) != '\n' && c != EOF)
  839.     ;
  840.   if (ferror (stdin))
  841.     perror_fatal ("input error");
  842. }
  843.  
  844.  
  845. /* interpret an edit command */
  846. static int
  847. edit (left, lenl, right, lenr, outfile)
  848.      struct line_filter *left;
  849.      int lenl;
  850.      struct line_filter *right;
  851.      int lenr;
  852.      FILE *outfile;
  853. {
  854.   for (;;)
  855.     {
  856.       int cmd0, cmd1;
  857.       int gotcmd = 0;
  858.  
  859.       cmd1 = 0; /* Pacify `gcc -W'.  */
  860.  
  861.       while (!gotcmd)
  862.     {
  863.       if (putchar ('%') != '%')
  864.         perror_fatal ("output error");
  865.       ck_fflush (stdout);
  866.  
  867.       cmd0 = skip_white ();
  868.       switch (cmd0)
  869.         {
  870.         case 'l': case 'r': case 's': case 'v': case 'q':
  871.           if (skip_white () != '\n')
  872.         {
  873.           give_help ();
  874.           flush_line ();
  875.           continue;
  876.         }
  877.           gotcmd = 1;
  878.           break;
  879.  
  880.         case 'e':
  881.           cmd1 = skip_white ();
  882.           switch (cmd1)
  883.         {
  884.         case 'l': case 'r': case 'b':
  885.           if (skip_white () != '\n')
  886.             {
  887.               give_help ();
  888.               flush_line ();
  889.               continue;
  890.             }
  891.           gotcmd = 1;
  892.           break;
  893.         case '\n':
  894.           gotcmd = 1;
  895.           break;
  896.         default:
  897.           give_help ();
  898.           flush_line ();
  899.           continue;
  900.         }
  901.           break;
  902.         case EOF:
  903.           if (feof (stdin))
  904.         {
  905.           gotcmd = 1;
  906.           cmd0 = 'q';
  907.           break;
  908.         }
  909.           /* falls through */
  910.         default:
  911.           flush_line ();
  912.           /* falls through */
  913.         case '\n':
  914.           give_help ();
  915.           continue;
  916.         }
  917.     }
  918.  
  919.       switch (cmd0)
  920.     {
  921.     case 'l':
  922.       lf_copy (left, lenl, outfile);
  923.       lf_skip (right, lenr);
  924.       return 1;
  925.     case 'r':
  926.       lf_copy (right, lenr, outfile);
  927.       lf_skip (left, lenl);
  928.       return 1;
  929.     case 's':
  930.       suppress_common_flag = 1;
  931.       break;
  932.     case 'v':
  933.       suppress_common_flag = 0;
  934.       break;
  935.     case 'q':
  936.       return 0;
  937.     case 'e':
  938.       if (! tmpname && ! (tmpname = private_tempnam ()))
  939.         perror_fatal ("temporary file name");
  940.  
  941.       tmpmade = 1;
  942.  
  943.       {
  944.         FILE *tmp = ck_fopen (tmpname, "w+");
  945.  
  946.         if (cmd1 == 'l' || cmd1 == 'b')
  947.           lf_copy (left, lenl, tmp);
  948.         else
  949.           lf_skip (left, lenl);
  950.  
  951.         if (cmd1 == 'r' || cmd1 == 'b')
  952.           lf_copy (right, lenr, tmp);
  953.         else
  954.           lf_skip (right, lenr);
  955.  
  956.         ck_fflush (tmp);
  957.  
  958.         {
  959.           int wstatus;
  960. #if ! HAVE_FORK
  961.           char *command = xmalloc (strlen (edbin) + strlen (tmpname) + 2);
  962.           sprintf (command, "%s %s", edbin, tmpname);
  963.           wstatus = system (command);
  964.           free (command);
  965. #else /* HAVE_FORK */
  966.           pid_t pid;
  967.  
  968.           ignore_SIGINT = 1;
  969.           checksigs ();
  970.  
  971.           pid = vfork ();
  972.           if (pid == 0)
  973.         {
  974.           char const *argv[3];
  975.           int i = 0;
  976.  
  977.           argv[i++] = edbin;
  978.           argv[i++] = tmpname;
  979.           argv[i++] = 0;
  980.  
  981.           execvp (edbin, (char **) argv);
  982.           write (STDERR_FILENO, edbin, strlen (edbin));
  983.           write (STDERR_FILENO, ": not found\n", 12);
  984.           _exit (1);
  985.         }
  986.  
  987.           if (pid < 0)
  988.         perror_fatal ("fork failed");
  989.  
  990.           while (waitpid (pid, &wstatus, 0) < 0)
  991.         if (errno == EINTR)
  992.           checksigs ();
  993.         else
  994.           perror_fatal ("wait failed");
  995.  
  996.           ignore_SIGINT = 0;
  997. #endif /* HAVE_FORK */
  998.  
  999.           if (wstatus != 0)
  1000.         fatal ("Subsidiary editor failed");
  1001.         }
  1002.  
  1003.         if (fseek (tmp, 0L, SEEK_SET) != 0)
  1004.           perror_fatal ("fseek");
  1005.         {
  1006.           /* SDIFF_BUFSIZE is too big for a local var
  1007.          in some compilers, so we allocate it dynamically.  */
  1008.           char *buf = xmalloc (SDIFF_BUFSIZE);
  1009.           size_t size;
  1010.  
  1011.           while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
  1012.         {
  1013.           checksigs ();
  1014.           ck_fwrite (buf, size, outfile);
  1015.         }
  1016.           ck_fclose (tmp);
  1017.  
  1018.           free (buf);
  1019.         }
  1020.         return 1;
  1021.       }
  1022.     default:
  1023.       give_help ();
  1024.       break;
  1025.     }
  1026.     }
  1027. }
  1028.  
  1029.  
  1030.  
  1031. /* Alternately reveal bursts of diff output and handle user commands.  */
  1032. static int
  1033. interact (diff, left, right, outfile)
  1034.      struct line_filter *diff;
  1035.      struct line_filter *left;
  1036.      struct line_filter *right;
  1037.      FILE *outfile;
  1038. {
  1039.   for (;;)
  1040.     {
  1041.       char diff_help[256];
  1042.       int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help));
  1043.  
  1044.       if (snarfed <= 0)
  1045.     return snarfed;
  1046.  
  1047.       checksigs ();
  1048.  
  1049.       switch (diff_help[0])
  1050.     {
  1051.     case ' ':
  1052.       puts (diff_help + 1);
  1053.       break;
  1054.     case 'i':
  1055.       {
  1056.         int lenl = atoi (diff_help + 1), lenr, lenmax;
  1057.         char *p = strchr (diff_help, ',');
  1058.  
  1059.         if (!p)
  1060.           fatal (diff_help);
  1061.         lenr = atoi (p + 1);
  1062.         lenmax = max (lenl, lenr);
  1063.  
  1064.         if (suppress_common_flag)
  1065.           lf_skip (diff, lenmax);
  1066.         else
  1067.           lf_copy (diff, lenmax, stdout);
  1068.  
  1069.         lf_copy (left, lenl, outfile);
  1070.         lf_skip (right, lenr);
  1071.         break;
  1072.       }
  1073.     case 'c':
  1074.       {
  1075.         int lenl = atoi (diff_help + 1), lenr;
  1076.         char *p = strchr (diff_help, ',');
  1077.  
  1078.         if (!p)
  1079.           fatal (diff_help);
  1080.         lenr = atoi (p + 1);
  1081.         lf_copy (diff, max (lenl, lenr), stdout);
  1082.         if (! edit (left, lenl, right, lenr, outfile))
  1083.           return 0;
  1084.         break;
  1085.       }
  1086.     default:
  1087.       fatal (diff_help);
  1088.       break;
  1089.     }
  1090.     }
  1091. }
  1092.  
  1093.  
  1094.  
  1095. /* temporary lossage: this is torn from gnu libc */
  1096. /* Return nonzero if DIR is an existing directory.  */
  1097. static int
  1098. diraccess (dir)
  1099.      char const *dir;
  1100. {
  1101.   struct stat buf;
  1102.   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
  1103. }
  1104.  
  1105. #if ! HAVE_TMPNAM
  1106.  
  1107. /* Return zero if we know that FILE does not exist.  */
  1108. static int
  1109. exists (file)
  1110.      char const *file;
  1111. {
  1112.   struct stat buf;
  1113.   return stat (file, &buf) == 0 || errno != ENOENT;
  1114. }
  1115.  
  1116. /* These are the characters used in temporary filenames.  */
  1117. static char const letters[] =
  1118.   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  1119.  
  1120. /* Generate a temporary filename and return it (in a newly allocated buffer).
  1121.    Use the prefix "dif" as in tempnam.
  1122.    This goes through a cyclic pattern of all possible
  1123.    filenames consisting of five decimal digits of the current pid and three
  1124.    of the characters in `letters'.  Each potential filename is
  1125.    tested for an already-existing file of the same name, and no name of an
  1126.    existing file will be returned.  When the cycle reaches its end
  1127.    return 0.  */
  1128. static char *
  1129. private_tempnam ()
  1130. {
  1131.   char const *dir = getenv (TMPDIR_ENV);
  1132.   static char const tmpdir[] = PVT_tmpdir;
  1133.   size_t index;
  1134.   char *buf;
  1135.   pid_t pid = getpid ();
  1136.   size_t dlen;
  1137.  
  1138.   if (!dir)
  1139.     dir = tmpdir;
  1140.  
  1141.   dlen = strlen (dir);
  1142.  
  1143.   /* Remove trailing slashes from the directory name.  */
  1144.   while (dlen && dir[dlen - 1] == '/')
  1145.     --dlen;
  1146.  
  1147.   buf = xmalloc (dlen + 1 + 3 + 5 + 1 + 3 + 1);
  1148.  
  1149.   sprintf (buf, "%.*s/.", (int) dlen, dir);
  1150.   if (diraccess (buf))
  1151.     {
  1152.       for (index = 0;
  1153.        index < ((sizeof (letters) - 1) * (sizeof (letters) - 1)
  1154.             * (sizeof (letters) - 1));
  1155.        ++index)
  1156.     {
  1157.       /* Construct a file name and see if it already exists.
  1158.  
  1159.          We use a single counter in INDEX to cycle each of three
  1160.          character positions through each of 62 possible letters.  */
  1161.  
  1162.       sprintf (buf, "%.*s/dif%.5lu.%c%c%c", (int) dlen, dir,
  1163.            (unsigned long) pid % 100000,
  1164.            letters[index % (sizeof (letters) - 1)],
  1165.            letters[(index / (sizeof (letters) - 1))
  1166.                % (sizeof (letters) - 1)],
  1167.            letters[index / ((sizeof (letters) - 1) *
  1168.                      (sizeof (letters) - 1))]);
  1169.  
  1170.       if (!exists (buf))
  1171.         return buf;
  1172.     }
  1173.       errno = EEXIST;
  1174.     }
  1175.  
  1176.   /* Don't free buf; `free' might change errno.  We'll exit soon anyway.  */
  1177.   return 0;
  1178. }
  1179.  
  1180. #endif /* ! HAVE_TMPNAM */
  1181.